package com.zemo.base.common.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * 该类对Jedis-Apiapi提供简单封装, 调用方请务必关心异常时的返回值
 * <p>
 * 由于时间关系,部分生僻api未经严格测试，请调用方自行测试
 * <p>
 * 欢迎添加更多高级方法
 * create by zemochen 2018/01/04
 */
public class RedisUtil {

    private static final Logger log = LoggerFactory.getLogger(RedisUtil.class);

    private JedisPool jedisPool;

    public RedisUtil(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    /**
     * @param key
     * @return key不存在或者出错都返回null
     */
    public String get(String key) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.get(key);
        } catch (Exception e) {
            log.error("redis get error", e);
            return null;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 获取多个key的值
     *
     * @param keys
     * @return List
     */
    public List<String> mget(String... keys) {
        Jedis jedis = getJedis();
        try {
            return jedis.mget(keys);
        } catch (Exception e) {
            log.error("redis mget error", e);
            return Collections.emptyList();
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 该操作具有原子性， 设置新值，并返回老值
     *
     * @param key
     * @param value
     * @return
     */
    public String getSet(String key, String value) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.getSet(key, value);
        } catch (Exception e) {
            log.error("redis getSet error", e);
            return null;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 设置一个简单key-value
     *
     * @param key
     * @param val
     * @return return OK if success
     */
    public String set(String key, String val) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.set(key, val);
        } catch (Exception e) {
            log.error("redis set error", e);
            return "error";
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 设置key，value对，该方法具有原子性
     *
     * @param keyvalues
     * @return OK 成功，否则失败
     */
    public String mset(String... keyvalues) {
        Jedis jedis = getJedis();
        try {
            return jedis.mset(keyvalues);
        } catch (Exception e) {
            log.error("redis操作发生异常", e);
            return "error";
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 该命令相当于set+expire ,区别是该方法具有原子性
     *
     * @param key
     * @param seconds
     * @param val
     * @return return OK if success
     */
    public String setex(String key, int seconds, String val) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.setex(key, seconds, val);
        } catch (Exception e) {
            log.error("redis setex error ", e);
            return "error";
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 设置一个key并指定过期时间，当且仅当key不存在时才成功
     *
     * @param key
     * @param value
     * @param expireSeconds
     * @return return OK 如果key不存在且设置成功，值已经存在及redis异常，都返回null
     */
    public String setnx(String key, String value, int expireSeconds) {
        Jedis jedis = getJedis();
        try {
            return jedis.set(key, value, "NX", "EX", expireSeconds);
        } catch (Exception e) {
            log.error("redis操作发生异常", e);
            return null;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * set if key not exists
     *
     * @param key
     * @param value
     * @return 如果设置成功返回1，否则返回0
     */
    public Long setnx(String key, String value) {
        Jedis jedis = getJedis();
        try {
            return jedis.setnx(key, value);
        } catch (Exception e) {
            log.error("redis setnx error", e);
            return 0L;
        } finally {
            quietlyClose(jedis);
        }
    }


    /**
     * 设置key过期时间
     * <p>
     * 从redis 2.1.3开始支持重新设置key的过期时间
     *
     * @param key
     * @param seconds
     * @return 设置成功返回1, 异常返回-1
     */
    public Long expire(String key, int seconds) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.expire(key, seconds);
        } catch (Exception e) {
            log.error("redis expire error", e);
            return -1L;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 将带有过期时间的key转为永久key, 与expire对应
     *
     * @param key
     * @return
     */
    public boolean persist(String key) {
        Jedis jedis = getJedis();
        try {
            return jedis.persist(key) == 1;
        } catch (Exception e) {
            log.error("redis persist error", e);
            return false;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 删除指定key
     *
     * @param key
     * @return 返回删除key个数
     */
    public Long del(String... key) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.del(key);
        } catch (Exception e) {
            log.error("redis del error", e);
            return 0L;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 判定指定key是否存在
     *
     * @param key
     * @return 异常及key不存在都返回false
     */
    public boolean exists(String key) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.exists(key);
        } catch (Exception e) {
            log.error("redis exists error", e);
            return false;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 自增操作
     *
     * @param key
     * @return 返回增长后的数据，如果异常返回-1
     */
    public Long incr(String key) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.incr(key);
        } catch (Exception e) {
            log.error("redis incr error", e);
            return -1L;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 自减操作
     *
     * @param key
     * @return 返回自减后的值，如果异常返回-1
     */
    public Long decr(String key) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.decr(key);
        } catch (Exception e) {
            log.error("redis decr error", e);
            return -1L;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * @param ckey
     * @param amount
     * @return 操作完成后的值，如果异常返回-1
     */
    public Long decrBy(String ckey, long amount) {
        Jedis jedis = getJedis();
        try {
            return jedis.decrBy(ckey, amount);
        } catch (Exception e) {
            log.error("redis descBy error", e);
            return -1L;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * incr的变种，可指定步长值
     *
     * @param key
     * @param amount
     * @return 增长后的值，异常返回-1
     */
    public Long incrBy(String key, long amount) {
        Jedis jedis = getJedis();
        try {
            return jedis.incrBy(key, amount);
        } catch (Exception e) {
            log.error("redis incrBy error", e);
            return -1L;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * map结构的自增操作
     *
     * @param key
     * @param field
     * @param value
     * @return 对应域自增后的值，如果异常返回-1
     */
    public Long hincrBy(String key, String field, long value) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.hincrBy(key, field, value);
        } catch (Exception e) {
            log.error("redis hincrBy error", e);
            return -1L;
        } finally {
            quietlyClose(jedis);
        }
    }


    /**
     * @param key
     * @param val
     * @return list size ,if error return -1
     */
    public Long rpush(String key, String... val) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.rpush(key, val);
        } catch (Exception e) {
            log.error("redis rpush error", e);
            return -1L;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * @param key
     * @param val
     * @return
     */
    public Long lpush(String key, String... val) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.lpush(key, val);
        } catch (Exception e) {
            log.error("redis lpush error", e);
            return -1L;
        } finally {
            quietlyClose(jedis);
        }
    }


    /**
     * @param key
     * @return return -1 if error
     */
    public Long llen(String key) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.llen(key);
        } catch (Exception e) {
            log.error("redis llen error", e);
            return -1L;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * @param key
     * @param start
     * @param end
     * @return empty list if error
     */
    public List lrange(String key, long start, long end) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.lrange(key, start, end);
        } catch (Exception e) {
            log.error("redis lrange error", e);
            return Collections.emptyList();
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * @param key
     * @param index
     * @param value
     * @return 'OK' if success
     */
    public String lset(String key, long index, String value) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.lset(key, index, value);
        } catch (Exception e) {
            log.error("redis lset error", e);
            return "error";
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * @param key
     * @return 没有元素可pop及异常都返回null
     */
    public String lpop(String key) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.lpop(key);
        } catch (Exception e) {
            log.error("redis lpop error", e);
            return null;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * @param key
     * @return 没有元素可pop及异常都返回null
     */
    public String rpop(String key) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.rpop(key);
        } catch (Exception e) {
            log.error("redis rpop error", e);
            return null;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * @param key
     * @param count
     * @param val
     * @return 成功删除元素的个数，如果异常返回0
     */
    public long lrem(String key, long count, String val) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.lrem(key, count, val);
        } catch (Exception e) {
            log.error("redis lrem error", e);
            return 0;
        } finally {
            quietlyClose(jedis);
        }
    }


    /**
     * 添加多个元素到set
     *
     * @param key
     * @param member
     * @return 返回添加成功的成员数, 异常返回0
     */
    public void sadd(String key, String... member) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            jedis.sadd(key, member);
            return;
        } catch (Exception e) {
            log.error("redis sadd error", e);
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 随机移除一个et集合中元素
     *
     * @param key
     * @return 空set或者key不存在及异常都返回null
     */
    public String spop(String key) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.spop(key);
        } catch (Exception e) {
            log.error("redis spop", e);
            return null;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 返回set集合的所有元素
     *
     * @param key
     * @return key不存在，或者set为空及异常都返回空set
     */
    public Set<String> smembers(String key) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.smembers(key);
        } catch (Exception e) {
            log.error("redis smembers", e);
            return Collections.emptySet();
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 返回set集合元素个数
     *
     * @param key
     * @return key不存在或者集合为空或者异常都返回0
     */
    public Long scard(String key) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.scard(key);
        } catch (Exception e) {
            log.error("redis操作发生异常", e);
            return 0L;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 从set中删除指定的元素
     *
     * @param key
     * @param val
     * @return 删除元素的个数，如果出错将返回0
     */
    public Long srem(String key, String... val) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.srem(key, val);
        } catch (Exception e) {
            log.error("redis srem", e);
            return 0L;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 判定指定元素是否是set的成员
     *
     * @param key
     * @param member
     * @return 如果是set的成员返回true，否则返回false
     */
    public boolean sismember(String key, String member) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.sismember(key, member);
        } catch (Exception e) {
            log.error("redis sismember error", e);
            return false;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 获取map指定field的值
     *
     * @param key
     * @param field
     * @return 返回对应域的值，如果异常返回null
     */
    public String hget(String key, String field) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.hget(key, field);
        } catch (Exception e) {
            log.error("redis hget error", e);
            return null;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 删除map里的指定key
     *
     * @param key
     * @param fields
     * @return 删除成功返回1，key不存在及异常返回0
     */
    public Long hdel(String key, String... fields) {
        Jedis jedis = null;
        Object var5;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.hdel(key, fields);
        } catch (Exception e) {
            log.error("redis操作发生异常", e);
            return 0L;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 如果map中已经存在指定key，则更新，否则新建
     *
     * @param key
     * @param field
     * @param value
     * @return 如果新key创建返回1，更新value返回0，异常也返回0
     */
    public Long hset(String key, String field, String value) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.hset(key, field, value);
        } catch (Exception e) {
            log.error("redis hset error", e);
            return 0L;
        } finally {
            quietlyClose(jedis);
        }
    }


    /**
     * 如果map的指定field不存在，那么设置值，否则不设置
     *
     * @param key
     * @param field
     * @param value
     * @return 设置值成功返回1，未设置及异常都返回0
     */
    public Long hsetnx(String key, String field, String value) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.hsetnx(key, field, value);
        } catch (Exception e) {
            log.error("redis hsetnx error", e);
            return 0L;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 获取map所有的field
     *
     * @param key
     * @return 异常返回空的map
     */
    public Map<String, String> hgetAll(String key) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.hgetAll(key);
        } catch (Exception e) {
            log.error("redis hgetAll error", e);
            return new HashMap<>(0);
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 设置多个field
     *
     * @param key
     * @param hash
     * @return 如果执行成功返回OK ,否则失败
     */
    public String hmset(String key, Map<String, String> hash) {
        Jedis jedis = null;
        String var4;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.hmset(key, hash);
        } catch (Exception e) {
            log.error("redis hmset error", e);
            return "error";
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 判定指定map的对应域是否存在
     *
     * @param key
     * @param field
     * @return true存在，否则不存在
     */
    public boolean hexists(String key, String field) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.hexists(key, field);
        } catch (Exception e) {
            log.error("redis hexists error", e);
            return false;
        } finally {
            quietlyClose(jedis);
        }
    }

    /**
     * 返回map的size
     *
     * @param key
     * @return 异常时返回0
     */
    public Long hlen(String key) {
        Jedis jedis = null;
        try {
            jedis = this.jedisPool.getResource();
            return jedis.hlen(key);
        } catch (Exception e) {
            log.error("redis hlen error", e);
            return 0L;
        } finally {
            quietlyClose(jedis);
        }
    }


    /**
     * 尝试获取锁，立即返回结果
     * 注：如果tryLock成功, 那么需要在适合的时间执行releaseLock
     *
     * @param key
     * @return true, 如果可以获取到锁
     */
    public boolean tryLock(String key) {
        return setnx(key, "1") == 1;
    }


    /**
     * 尝试获取锁，并设置锁有的有效时间, 立即返回结果
     *
     * @param key
     * @param expireSeconds 锁过期时间
     * @return
     */
    public boolean tryLock(String key, int expireSeconds) {
        String value = setnx(key, "1", expireSeconds);
        return value != null;
    }


    /**
     * 获取锁，如果当前无法获取锁，将阻塞进行等待，直到timeout
     *
     * @param key
     * @param timeoutMillis 等待时间，毫秒数
     * @return
     */
    public boolean tryLockAndWait(String key, Long timeoutMillis) {
        long startTime = System.currentTimeMillis();
        do {
            try {
                TimeUnit.MILLISECONDS.sleep(50);
                if (tryLock(key)) {
                    return true;
                }
            } catch (InterruptedException e) {
                return false;
            }
        } while (System.currentTimeMillis() - startTime <= timeoutMillis);
        return false;
    }

    /**
     * 释放锁
     *
     * @param key
     */
    public void releaseLock(String key) {
        del(key);
    }

    public Jedis getJedis() {
        return jedisPool.getResource();
    }


    public void quietlyClose(Jedis jedis) {
        try {
            if (jedis != null) {
                jedis.close();
            }
        } catch (Exception e) {
            //pass
        }
    }

    public void setJedisPool(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

}
